/*
migrationManager.c

Author: Jesse Niekraszewicz
Date: April 5, 2012

For: SYSC 4907 Engineering Project

Description: 
receives "migration messages" from the Migration Simulator (simulator.c)
and performs NAT on incoming and outgoing packet from the Migration Simulator
to demonstrate the feasibility of performing virtual machine migration
with an IP address change

Arguments: none
*/

#define __USE_BSD
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#define __FAVOR_BSD
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <arpa/inet.h>

#include <stdio.h> //for printf
#include <stdlib.h> //for malloc
#include <signal.h> //for handeling inturrupts (terminate inturrupt)
#include <string.h> //for strcopy
#include <linux/netfilter.h>
#include <libnetfilter_queue/libnetfilter_queue.h>
#include <pthread.h>
#include <errno.h>

#define TCP_P 0x6
#define UDP_P 0x11
#define maxNumOfVMConnections 5
#define MIG_MAN_PORT 8888


struct thread_params {
	char old_ip[16];
	char new_ip[16];
};

struct thread_params *ipPairs[maxNumOfVMConnections];


//used to calculate checksums (from http://www.bytebot.net/rrjcode/ratelimit-0.1/)
static int SumWords(u_int16_t *buf, int nwords){
	
  register u_int32_t    sum = 0;

  while (nwords >= 16){
	  
    sum += (u_int16_t) ntohs(*buf++);
    sum += (u_int16_t) ntohs(*buf++);
    sum += (u_int16_t) ntohs(*buf++);
    sum += (u_int16_t) ntohs(*buf++);
    sum += (u_int16_t) ntohs(*buf++);
    sum += (u_int16_t) ntohs(*buf++);
    sum += (u_int16_t) ntohs(*buf++);
    sum += (u_int16_t) ntohs(*buf++);
    sum += (u_int16_t) ntohs(*buf++);
    sum += (u_int16_t) ntohs(*buf++);
    sum += (u_int16_t) ntohs(*buf++);
    sum += (u_int16_t) ntohs(*buf++);
    sum += (u_int16_t) ntohs(*buf++);
    sum += (u_int16_t) ntohs(*buf++);
    sum += (u_int16_t) ntohs(*buf++);
    sum += (u_int16_t) ntohs(*buf++);
    nwords -= 16;
  }
  while (nwords--)
    sum += (u_int16_t) ntohs(*buf++);
  return(sum);
}


//computes the IP Checksum and places it in IP header (from http://www.bytebot.net/rrjcode/ratelimit-0.1/)
void IpChecksum(struct ip *ip){
	
  register u_int32_t    sum;

  ip->ip_sum = 0;
  sum = SumWords((u_int16_t *) ip, ip->ip_hl << 1);

  sum = (sum >> 16) + (sum & 0xFFFF);
  sum += (sum >> 16);
  sum = ~sum;

  ip->ip_sum = htons(sum);
}


//computes the TCP checksum and places it in the TCP header (from http://www.bytebot.net/rrjcode/ratelimit-0.1/)
void TcpChecksum(struct ip *ip)
{
  struct tcphdr *const tcp = (struct tcphdr *) ((long *) ip + ip->ip_hl);
  u_int32_t     sum;
  int           tcp_len;

  tcp->th_sum = 0;

  tcp_len = (u_int16_t) ntohs(ip->ip_len) - (ip->ip_hl << 2);

  sum = SumWords((u_int16_t *) &ip->ip_src, 4);
  sum += (u_int16_t) IPPROTO_TCP;
  sum += (u_int16_t) tcp_len;

  sum += SumWords((u_int16_t *) tcp, tcp_len >> 1);

        if (tcp_len & 1)
        {
                sum += (u_int16_t) (((u_char *) tcp)[tcp_len - 1] << 8);
        }

  sum = (sum >> 16) + (sum & 0xFFFF);
  sum += (sum >> 16);
  sum = ~sum;

  tcp->th_sum = htons(sum);
}


//computes the UDP checksum and places it in the UDP header (from http://www.bytebot.net/rrjcode/ratelimit-0.1/)
void UdpChecksum(struct ip *ip)
{
  struct udphdr *const udp = (struct udphdr *) ((long *) ip + ip->ip_hl);
  u_int32_t     sum;

  udp->uh_sum = 0;

  sum = SumWords((u_int16_t *) &ip->ip_src, 4);
  sum += (u_int16_t) IPPROTO_UDP;
  sum += (u_int16_t) ntohs(udp->uh_ulen);

  sum += SumWords((u_int16_t *) udp, ((u_int16_t) ntohs(udp->uh_ulen)) >> 1);
  if (ntohs(udp->uh_ulen) & 1)
    sum += (u_int16_t) (((u_char *) udp)[ntohs(udp->uh_ulen) - 1] << 8);

  sum = (sum >> 16) + (sum & 0xFFFF);
  sum += (sum >> 16);
  sum = ~sum;

  udp->uh_sum = htons(sum);
}


//function called when a packet is received
static int Callback_in(struct nfq_q_handle *queue, struct nfgenmsg *msg, struct nfq_data *pkt, void *cbData){
	
	struct thread_params *ip_data = (struct thread_params *)cbData;
	uint32_t id = 0;
	struct nfqnl_msg_packet_hdr *header;
	int dataLength;
	char *pktData;
	struct in_addr tempIP;
	int x=0;
	
	
	if (header = nfq_get_msg_packet_hdr(pkt)){
		//get id assigned to packet by netfilter
		id = ntohl(header->packet_id);
	}
	
	//get packet size
	dataLength = nfq_get_payload(pkt, &pktData);
	
	//get IP header
	struct ip *iphdr = (struct ip *)pktData;
	
	if (dataLength >= 0){
		//output packet size, src ip
		//printf("ID: %u; Packet Size: %d bytes; Src IP: %s; \n",id ,dataLength, (char *)inet_ntoa(iphdr->ip_src));
	
		//incomming packet -> get current src IP (the newIP)

		//find corressponding oldIP
		for (x=0;x<maxNumOfVMConnections;x++){
			if (strcmp(ipPairs[x]->new_ip, inet_ntoa(iphdr->ip_src))==0){
				inet_aton(ipPairs[x]->old_ip, &tempIP);
				//set new src IP to old src IP
				iphdr->ip_src = tempIP;
				//printf("replaced src IP: %s with src IP: %s \n\n", ipPairs[x]->new_ip, ipPairs[x]->old_ip);
			}
		}
	}
	
	//compute IP checksum
	IpChecksum(iphdr);
	
	if (iphdr->ip_p == TCP_P){
		//compute TCP checksum
		TcpChecksum(iphdr);
	}
	
	if (iphdr->ip_p == UDP_P){
		//compute UDP checksum
		UdpChecksum(iphdr);
	}
	
	//accept the packet
	return nfq_set_verdict(queue, id, NF_ACCEPT, dataLength, (unsigned char *)pktData);
}


//set up queue, handle and call the Callback function for incomming traffic
void *handle_incomming(void *arg){
	
	struct nfq_handle *netfilterHandle;
	struct nfnl_handle *netlinkHandle;
	struct nfq_q_handle *queue;
	
	int fd, res;
	char buf[4049];

	//get a netfilter queue connection handle
	if (!(netfilterHandle = nfq_open())){
		printf("E001: Failed to get a nf queue connection handle\n");
	}
	printf("stage 1 - success!\n");
	
	//unbind the handler from processing IP packets
	if (nfq_unbind_pf(netfilterHandle, AF_INET) < 0){
		printf("E002: Failed to unbind from processing IP packets\n");
	}
		printf("stage 2 - success!\n");
		
	//bind the handler to process IP packets
	if (nfq_bind_pf(netfilterHandle, AF_INET) < 0){
		printf("E003: Failed to bind to process IP packets\n");		
	}
		printf("stage 3 - success!\n");
		
	//create a queue 0 and install the callback function on queue 0
	if (!(queue = nfq_create_queue(netfilterHandle, 0, &Callback_in, arg))){
		printf("E004: Failed to create queue\n");
	}
		printf("stage 4 - success!\n");
		
	//turn on packet copy mode
	if (nfq_set_mode(queue, NFQNL_COPY_PACKET, 0xffff) < 0){
		printf("E005: Failed to set packet copy mode\n");
	}
	printf("stage 5 - success!\n");
		
	netlinkHandle = nfq_nfnlh(netfilterHandle);
	fd = nfnl_fd(netlinkHandle);
	
	
	while ((res = recv(fd, buf, sizeof(buf), 0)) && res >=-2){ 
		//during FTP transfer an error sometimes occurs causing recv to return a -1
		//call callback function
		nfq_handle_packet(netfilterHandle, buf, res);
	}
	
	printf("destroying queue...\n");
	//destroy the queue
	nfq_destroy_queue(queue);
	//close the connection handle
	nfq_close(netfilterHandle);
	
	printf("Exiting...\n");	
}


//function called when a packet is received
static int Callback_out(struct nfq_q_handle *queue, struct nfgenmsg *msg, struct nfq_data *pkt, void *cbData){

	struct thread_params *ip_data = (struct thread_params *)cbData;	
	uint32_t id = 0;
	struct nfqnl_msg_packet_hdr *header;
	int dataLength;
	char *pktData;
	struct in_addr tempIP;
	int x=0;
	
	
	if (header = nfq_get_msg_packet_hdr(pkt)){
		//get id assigned to packet by netfilter
		id = ntohl(header->packet_id);
	}
	
	//get packet size
	dataLength = nfq_get_payload(pkt, &pktData);
	
	//get IP header
	struct ip *iphdr = (struct ip *)pktData;
	
	if (dataLength >= 0){
		//output packet size, src ip
		//printf("ID: %u; Packet Size: %d bytes; Dst IP: %s; \n",id ,dataLength, (char *)inet_ntoa(iphdr->ip_dst));
	
		//outgoing packet -> get current dst IP (the oldIP)

		//find corressponding newIP
		for (x=0;x<maxNumOfVMConnections;x++){
			if (strcmp(ipPairs[x]->old_ip, inet_ntoa(iphdr->ip_dst))==0){
				inet_aton(ipPairs[x]->new_ip, &tempIP);
				//set old dst IP to new dst IP
				iphdr->ip_dst = tempIP;
			//	printf("replaced dst IP: %s with dst IP: %s \n\n", ipPairs[x]->old_ip, ipPairs[x]->new_ip);
			}
		}
	}
	
	//compute IP checksum
	IpChecksum(iphdr);
	
	if (iphdr->ip_p == TCP_P){
		//compute TCP checksum
		TcpChecksum(iphdr);
	}
	
	if (iphdr->ip_p == UDP_P){
		//compute UDP checksum
		UdpChecksum(iphdr);
	}

	//accept the packet
	return nfq_set_verdict(queue, id, NF_ACCEPT, dataLength, (unsigned char *)pktData);
}


//set up queue, handle and call the Callback function for outgoing traffic
void *handle_outgoing(void *arg){
	
	struct nfq_handle *netfilterHandle;
	struct nfnl_handle *netlinkHandle;
	struct nfq_q_handle *queue;
	
	int fd, res;
	char buf[4049];

	//get a netfilter queue connection handle
	if (!(netfilterHandle = nfq_open())){
		printf("E001: Failed to get a nf queue connection handle\n");
		return;
	}
	printf("stage 1 - success!\n");
	
	//unbind the handler from processing IP packets
	if (nfq_unbind_pf(netfilterHandle, AF_INET) < 0){
		printf("E002: Failed to unbind from processing IP packets\n");
		return;
	}
		printf("stage 2 - success!\n");
		
	//bind the handler to process IP packets
	if (nfq_bind_pf(netfilterHandle, AF_INET) < 0){
		printf("E003: Failed to bind to process IP packets\n");	
		return;
	}
		printf("stage 3 - success!\n");
		
	//create a queue 1 and install the callback function on queue 1
	if (!(queue = nfq_create_queue(netfilterHandle, 1, &Callback_out, arg))){
		printf("E004: Failed to create queue\n");
		return;
	}
		printf("stage 4 - success!\n");
		
	//turn on packet copy mode
	if (nfq_set_mode(queue, NFQNL_COPY_PACKET, 0xffff) < 0){
		printf("E005: Failed to set packet copy mode\n");
		return;
	}
	printf("stage 5 - success!\n");
		
	netlinkHandle = nfq_nfnlh(netfilterHandle);
	fd = nfnl_fd(netlinkHandle);
	
	while ((res = recv(fd, buf, sizeof(buf), 0)) && res >=-2){
		//during FTP transfer an error sometimes occurs causing recv to return a -1
		//call callback function
		nfq_handle_packet(netfilterHandle, buf, res);
	}
	
	printf("destroying queue...\n");
	//destroy the queue
	nfq_destroy_queue(queue);
	//close the connection handle
	nfq_close(netfilterHandle);
	
	printf("Exiting...\n");	
	return;
}


//function called when a packet with the FIN flag set is received
static int Callback_FIN(struct nfq_q_handle *queue, struct nfgenmsg *msg, struct nfq_data *pkt, void *cbData){

	struct thread_params *ip_data = (struct thread_params *)cbData;	
	uint32_t id = 0;
	struct nfqnl_msg_packet_hdr *header;
	int dataLength;
	char *pktData;;
	int x=0;
	char cmd[256];

	
	if (header = nfq_get_msg_packet_hdr(pkt)){
		//get id assigned to packet by netfilter
		id = ntohl(header->packet_id);
	}
	
	//get packet size
	dataLength = nfq_get_payload(pkt, &pktData);
	
	//get IP header
	struct ip *iphdr = (struct ip *)pktData;
	
	if (dataLength >= 0){
		//incoming packet
	
		//find the index in ipPairs associated with this IP
		for (x=0; x<maxNumOfVMConnections; x++){
			//if (strcmp(ipPairs[x]->old_ip,inet_ntoa(iphdr->ip_dst))==0 || strcmp(ipPairs[x]->new_ip,inet_ntoa(iphdr->ip_src))==0){
			if (strcmp(ipPairs[x]->new_ip,inet_ntoa(iphdr->ip_src))==0){
				printf("\nreceived FIN packet with src: %s\n", inet_ntoa(iphdr->ip_src));
				printf("\n removing iptables entried for ipPair(new,old): %s, %s\n",ipPairs[x]->new_ip,ipPairs[x]->old_ip);
				//remove IPtables for this IP pair
				sprintf(cmd,"iptables -t mangle -D PREROUTING -s %s -j NFQUEUE --queue-num 0",ipPairs[x]->new_ip);
				system(cmd);
				sprintf(cmd,"iptables -t mangle -D PREROUTING -s %s --protocol tcp --tcp-flags FIN FIN -j NFQUEUE --queue-num 2",ipPairs[x]->new_ip);
				system(cmd);
				sprintf(cmd,"iptables -t mangle -D OUTPUT -d %s -j NFQUEUE --queue-num 1",ipPairs[x]->old_ip);
				system(cmd);
			
				//remove this ipPair
				strcpy(ipPairs[x]->old_ip,"0.0.0.0");
				strcpy(ipPairs[x]->new_ip,"0.0.0.0");
			}
		}
	}
	
	//accept the packet
	return nfq_set_verdict(queue, id, NF_ACCEPT, dataLength, (unsigned char *)pktData);
}


//set up queue, handle and call the Callback function for traffic with FIN flag set
void *handle_FIN(void *arg){
	
	struct nfq_handle *netfilterHandle;
	struct nfnl_handle *netlinkHandle;
	struct nfq_q_handle *queue;
	
	int fd, res;
	char buf[4049];

	//get a netfilter queue connection handle
	if (!(netfilterHandle = nfq_open())){
		printf("E001: Failed to get a nf queue connection handle\n");
		return;
	}
	printf("stage 1 - success!\n");
	
	//unbind the handler from processing IP packets
	if (nfq_unbind_pf(netfilterHandle, AF_INET) < 0){
		printf("E002: Failed to unbind from processing IP packets\n");
		return;
	}
		printf("stage 2 - success!\n");
		
	//bind the handler to process IP packets
	if (nfq_bind_pf(netfilterHandle, AF_INET) < 0){
		printf("E003: Failed to bind to process IP packets\n");	
		return;
	}
		printf("stage 3 - success!\n");
		
	//create a queue 2 and install the callback function on queue 2
	if (!(queue = nfq_create_queue(netfilterHandle, 2, &Callback_FIN, arg))){
		printf("E004: Failed to create queue\n");
		return;
	}
		printf("stage 4 - success!\n");
		
	//turn on packet copy mode
	if (nfq_set_mode(queue, NFQNL_COPY_PACKET, 0xffff) < 0){
		printf("E005: Failed to set packet copy mode\n");
		return;
	}
	printf("stage 5 - success!\n");
		
	netlinkHandle = nfq_nfnlh(netfilterHandle);
	fd = nfnl_fd(netlinkHandle);
	while ((res = recv(fd, buf, sizeof(buf), 0)) && res >=-2){
		//during FTP transfer an error sometimes occurs causing recv to return a -1
		//call callback function
		nfq_handle_packet(netfilterHandle, buf, res);
	}
	
	printf("destroying queue...\n");
	//destroy the queue
	nfq_destroy_queue(queue);
	//close the connection handle
	nfq_close(netfilterHandle);
	
	printf("Exiting...\n");	
	return;
}


//handle migration messages from the server
void *handle_migMessages(void *arg){
	int serverSocket, clientSocket;
	char *data;
	struct sockaddr_in myAddr, serverAddr;
	int bytesReceived;
	socklen_t sockLength;
	char oldIP[INET_ADDRSTRLEN];
	char *newIP;
	
	newIP = malloc(16);
	data = malloc(16);
	
	struct timeval start, end;

    long mtime, seconds, useconds;    

	
	//create a UDP socket 
	serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (serverSocket < 0){
		printf("ERROR: could not create socket\n");
		exit(1);
	}
	
	myAddr.sin_family = AF_INET;
	myAddr.sin_port = htons(MIG_MAN_PORT); //the port to bind to
	myAddr.sin_addr.s_addr = INADDR_ANY; // the IP Address to bind to
	bzero(&(myAddr.sin_zero),8); //set all bits to 0
	
	//bind to the socket
	if (bind(serverSocket, (struct sockaddr *) &myAddr, sizeof(myAddr)) < 0){
		printf("ERROR: could not bind to socket %i\n",MIG_MAN_PORT);
		printf( "Error : %s\n", strerror( errno ));
		exit(1);
	}	
	//listen on socket
	if (listen(serverSocket, 1) < 0){
		printf("ERROR: failed to listen on socket %i\n",MIG_MAN_PORT);
		printf( "Error : %s\n", strerror( errno ));
		exit(1);
	}
	sockLength = sizeof(serverAddr);

	while(1){
		//printf("Waiting for connection\n");
		clientSocket = accept(serverSocket, (struct sockaddr *) &serverAddr, &sockLength);
		gettimeofday(&start, NULL);
		if (clientSocket > 0){
			//recieve migration message from the server
			bytesReceived = recv(clientSocket, data, INET_ADDRSTRLEN, 0);
			if (bytesReceived < 0){
				printf("ERROR: Failed to recieve migration message\n");
				printf( "Error : %s\n", strerror( errno ));
			}else{
				data[bytesReceived]='\0';
				//turn the server's IP address into a string stored in oldIP
				inet_ntop(AF_INET, &serverAddr.sin_addr, oldIP, INET_ADDRSTRLEN);
				//display the message received
				printf("Received Message from %s: %s\n",oldIP,data);
				newIP = data;
				//&newIP+16 = "\0";
			}
			
			//inserts oldIP and newIP into the ipPairs structure and updates the iptables - returns 1 on success
			if (insertIpPair(oldIP,newIP)){
				gettimeofday(&end, NULL);
				seconds  = end.tv_sec  - start.tv_sec;
				useconds = end.tv_usec - start.tv_usec;

				mtime = ((seconds) * 1000000 + useconds);

				printf("THE SERVER HAS MIGRATED FROM %s TO %s IN %ld us \n",oldIP, newIP, mtime);
				
				gettimeofday(&start, NULL);
				sleep(1);
				gettimeofday(&end, NULL);
				seconds  = end.tv_sec  - start.tv_sec;
				useconds = end.tv_usec - start.tv_usec;

				mtime = ((seconds) * 1000000 + useconds);

				printf("1 sec = %ld us \n", mtime);

			}else{
				printf("UNKNOWN MESSAGE\n");
			}
		}else{
				printf("error");
		}
		close(clientSocket);
		sleep(1);
	}
	close(serverSocket);
}
		

void on_signal(int sig){

	//remove all the iptables entries
	char cmd[256];
	int x=0;
	for (x=0; x<maxNumOfVMConnections; x++){
		sprintf(cmd,"iptables -t mangle -D PREROUTING -s %s -j NFQUEUE --queue-num 0",ipPairs[x]->new_ip);
		system(cmd);
		sprintf(cmd,"iptables -t mangle -D PREROUTING -s %s --protocol tcp --tcp-flags FIN FIN -j NFQUEUE --queue-num 2",ipPairs[x]->new_ip);
		system(cmd);
		sprintf(cmd,"iptables -t mangle -D OUTPUT -d %s -j NFQUEUE --queue-num 1",ipPairs[x]->old_ip);
		system(cmd);
	}
	free(*ipPairs);
	signal(SIGINT, SIG_DFL);
}

//inserts a pair of oldIP and newIP recieved from the migration message into the ipPairs structure
//returns 1 on success, -1 on failure
int insertIpPair(char *oldIP, char *newIP){
	int firstEmptyElement = -1;
	void *validNewIP, *validOldIP;
	int x = 0;
	char cmd[256];
		
	firstEmptyElement = findFirstEmptyElementInIpPairs();
	
	if (firstEmptyElement == -1){
		printf("A max of %i connection can be handled. No more migrations can be handled at this time.",maxNumOfVMConnections);
		return -1;
		
	//copies oldIP to validOldIP and newIP to validNewIP - returns 1 on success
	}else if (!inet_pton(AF_INET, oldIP, &validOldIP) || !inet_pton(AF_INET, newIP, &validNewIP)){
			printf("failed to insert ipPair");
			return -1;
		
	//check if the server is "migrating to itself", if so it means the server is migrating back to it's original location
	//remove the ipPair with this IP set as oldIP		
	}else if (strcmp(oldIP,newIP)==0){
		//remove the ipPair with this IP set as oldIP
		for (x=0; x<maxNumOfVMConnections; x++){
			if (strcmp(ipPairs[x]->old_ip,oldIP)==0){
				printf("server migrated back to its old location.\n");
				printf("Removing iptables entry for ipPair(new,old): %s, %s\n",ipPairs[x]->new_ip,ipPairs[x]->old_ip);
				//remove IPtables for this IP pair
				sprintf(cmd,"iptables -t mangle -D PREROUTING -s %s -j NFQUEUE --queue-num 0",ipPairs[x]->new_ip);
				system(cmd);
				sprintf(cmd,"iptables -t mangle -D PREROUTING -s %s --protocol tcp --tcp-flags FIN FIN -j NFQUEUE --queue-num 2",ipPairs[x]->new_ip);
				system(cmd);
				sprintf(cmd,"iptables -t mangle -D OUTPUT -d %s -j NFQUEUE --queue-num 1",ipPairs[x]->old_ip);
				system(cmd);
			
				//remove this ipPair
				strcpy(ipPairs[x]->old_ip,"0.0.0.0");
				strcpy(ipPairs[x]->new_ip,"0.0.0.0");
			}
		}
	}else{
		printf("inserting ipPair");
		//set old_ip and new_ip to the passed IPs
		strcpy(ipPairs[firstEmptyElement]->old_ip, oldIP);
		strcpy(ipPairs[firstEmptyElement]->new_ip, newIP);
				
		//add the iptables entries
		char cmd[256];
		//hook packet before the routing decision is made on the packet
		sprintf(cmd,"iptables -t mangle -A PREROUTING -s %s -j NFQUEUE --queue-num 0",ipPairs[firstEmptyElement]->new_ip);
		system(cmd);
		//send packets with FIN set to a seperate queue
		sprintf(cmd,"iptables -t mangle -A PREROUTING -s %s --protocol tcp --tcp-flags FIN FIN -j NFQUEUE --queue-num 2",ipPairs[firstEmptyElement]->new_ip);
		system(cmd);
		//output table hooks packet before the final routing decision is made
		sprintf(cmd,"iptables -t mangle -A OUTPUT -d %s -j NFQUEUE --queue-num 1",ipPairs[firstEmptyElement]->old_ip);
		system(cmd);
		
		return 1;
	}
}


//finds and returns the first empty element of the ipPairs array
int findFirstEmptyElementInIpPairs(){
	int x = 0;
	//go through the array
	for (x=0;x<maxNumOfVMConnections;x++){
		//if the value of the IP is 0.0.0.0, the element is empty
		if (strcmp(ipPairs[x]->old_ip,"0.0.0.0")==0){
			//return the index of the array with an empty element
			return x;
		}
	}
	
	//no elements are empty
	return -1;
}
	

int main(int argc, char *argv[]){
	int x=0;		
	
	printf("starting - success!\n");	

	pthread_t thread1, thread2, thread3, thread4;
	int t1_status, t2_status, t3_status, t4_status;
	//initialize the values of the ipPairs array to empty
	for (x=0; x<maxNumOfVMConnections; x++){
		//reserve memory for the ipPairs array of oldIP-newIP pairs
		ipPairs[x] = (struct thread_params*){malloc(sizeof(struct thread_params)*maxNumOfVMConnections)};
		strcpy(ipPairs[x]->old_ip,"0.0.0.0");
		strcpy(ipPairs[x]->new_ip,"0.0.0.0");
	}
	
	printf("starting PREROUTING thread\n");
	//initialize thread 1 (handles incomming packets)
	t1_status = pthread_create(&thread1, NULL, handle_incomming, (void *) ipPairs);
	sleep(1);
	printf("starting OUTPUT thread\n");
	//initialize thread 2 (handles outgoing packets)
	t2_status = pthread_create(&thread2, NULL, handle_outgoing, (void *) ipPairs);
	sleep(1);
	printf("starting FIN thread\n");
	//initialize thread 3 (handles incomming FIN packets)
	t3_status = pthread_create(&thread3, NULL, handle_FIN, (void *) ipPairs);
	printf("starting Daemon thread\n");
	sleep(1);
	//initialize thread 4 (handles the migration messages from the server)
	t4_status = pthread_create(&thread4, NULL, handle_migMessages, (void *) ipPairs);

	signal(SIGINT, on_signal);
	
	pthread_join(thread1, NULL);
	pthread_join(thread2, NULL);
	pthread_join(thread3, NULL);
	pthread_join(thread4, NULL);


	return 0;
}
